package com.portal.exception.exception;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.portal.common.enums.ErrorEnum;
import com.portal.common.enums.StatusEnum;
import com.portal.common.response.JsonBaseResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.client.HttpStatusCodeException;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;

/**
 * 全局异常处理程序
 *
 * @author Amadeus
 * @date 2022/07/07
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private final Logger logger = LoggerFactory.getLogger(getClass());


    /**
     * 处理自定义异常
     */
    @ExceptionHandler(AppException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public JsonBaseResult<?> handleAppException(AppException e) {
        JsonBaseResult<Object> result = new JsonBaseResult<>();
        result.setResultStatus(StatusEnum.FAILED);
        result.setErrorCode(String.valueOf(e.getCode()));
        result.setErrorMessage(e.getMessage());
        return result;
    }


    @ExceptionHandler(HttpStatusCodeException.class)
    public ResponseEntity<String> handleStatusCodeException(HttpServletRequest request, HttpStatusCodeException ex) {
        logErrorInfo(request, ex);
        return new ResponseEntity<>(ex.getResponseBodyAsString(), ex.getStatusCode());
    }

    @ExceptionHandler(NumberFormatException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public JsonBaseResult<Object> handleNumberFormatException(HttpServletRequest request, NumberFormatException ex) {
        logErrorInfo(request, ex);
        JsonBaseResult<Object> result = new JsonBaseResult<>();
        result.setResultStatus(StatusEnum.FAILED);
        result.setErrorCode(ErrorEnum.DATA_PARAMETERS_ERROR);
        return result;
    }

    @ExceptionHandler(JsonParseException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public JsonBaseResult<Object> handleJsonParseException(HttpServletRequest request, JsonParseException ex) {
        logErrorInfo(request, ex);
        JsonBaseResult<Object> result = new JsonBaseResult<>();
        result.setResultStatus(StatusEnum.FAILED);
        result.setErrorCode(ErrorEnum.DATA_PARAMETERS_ERROR);
        return result;
    }

    @ExceptionHandler(InvalidFormatException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public JsonBaseResult<Object> handleInvalidFormatException(HttpServletRequest request, InvalidFormatException ex) {
        logErrorInfo(request, ex);
        JsonBaseResult<Object> result = new JsonBaseResult<>();
        result.setResultStatus(StatusEnum.FAILED);
        result.setErrorCode(ErrorEnum.DATA_PARAMETERS_ERROR);
        return result;
    }

    @ExceptionHandler(BindException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public JsonBaseResult<Object> handleBindException(HttpServletRequest request, BindException ex) {
        logErrorInfo(request, ex);
        JsonBaseResult<Object> result = new JsonBaseResult<>();
        result.setResultStatus(StatusEnum.FAILED);
        result.setErrorCode(ErrorEnum.DATA_PARAMETERS_ERROR);
        return result;
    }


    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public JsonBaseResult<Object> handleHttpMessageNotReadableException(HttpServletRequest request, HttpMessageNotReadableException ex) {
        logErrorInfo(request, ex);
        JsonBaseResult<Object> result = new JsonBaseResult<>();
        result.setResultStatus(StatusEnum.FAILED);
        result.setErrorCode(ErrorEnum.DATA_PARAMETERS_ERROR);
        return result;
    }

    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(value = HttpStatus.OK)
    @ResponseBody
    public JsonBaseResult<Object> runTimeException(HttpServletRequest request, RuntimeException ex) {
        logErrorInfo(request, ex);
        JsonBaseResult<Object> result = new JsonBaseResult<>();
        result.setResultStatus(StatusEnum.FAILED);
        result.setErrorCode(ErrorEnum.SYSTEM_EXCEPTION_ERROR);
        return result;
    }

    private void logErrorInfo(HttpServletRequest httpRequest, Exception ex) {
        String servletPath = httpRequest.getServletPath();
        logger.error("Error Request: " + httpRequest.getMethod() + " " + servletPath);
        String params;
        if (httpRequest.getContentType() == null || !httpRequest.getContentType().contains("x-www-form-urlencoded")) {
            params = getParams(httpRequest);
        } else {
            params = getFormParams(httpRequest);
        }
        logger.error("Params: " + params);
        logger.error("Exception caught by GlobalExceptionHandler:", ex);
    }

    private String getParams(HttpServletRequest httpRequest) {
        StringBuilder builder = new StringBuilder();
        try {
            try (BufferedReader reader = httpRequest.getReader()) {
                String line;
                while ((line = reader.readLine()) != null) {
                    builder.append(line);
                }
            } catch (IOException e) {
                logger.error("", e);
            }
        } catch (IllegalStateException e) {
            logger.error(e.getMessage());
        }
        return builder.toString();
    }

    private String getFormParams(HttpServletRequest httpRequest) {
        StringBuilder builder = new StringBuilder();
        httpRequest.getParameterMap().forEach((key, value) -> {
            builder.append(key).append("=");
            if (value.length > 0) {
                builder.append(value[0]).append(" ");
            } else {
                builder.append(" ");
            }
        });
        return builder.toString();
    }


}
